---
id: fbsimulatorcontrol
title: FBSimulatorControl
---

`FBSimulatorControl` is the macOS Framework that implements all functionality associated with iOS Simulators within `idb`. It can be used independently of `idb` as it is a standalone Framework.

## `CoreSimulator.framework`

`CoreSimulator` is the Private Framework that is the interface for most Simulator related functionality on macOS. In previous Xcode versions, `CoreSimulator` was bundled inside of Xcode, but it is now installed at the System level just like `MobileDevice.framework`. It may be upgraded to a newer version as part of the install process of Xcode itself.

`CoreSimulator` is used by Xcode and `simctl` as the Framework used to manipulate Simulators. It has Objective-C classes representing a set of Simulators within a directory (`SimDeviceSet` wrapped by `FBSimulatorSet`) and an individual iOS Simulator (`SimDevice`, wrapped by `FBSimulator`). There is also a Class that behaves a lot like an "entrypoint" to the Framework in `SimServiceContext`, this performs initialization of external services and is aware of the various configurations of Simulators that are availabile.

## `simctl`

`simctl` is essentially a CLI that exposes iOS Simulator functionality by linking and using `CoreSimulator`. This binary is bundled inside of Xcode, and typically addressed via usage of the `xcrun` command. `xcrun` is essentially a trampoline that addresses binaries that are bundled within Xcode by using the value defined in `xcode-select`

The overwhelming majority of Simulator functionality is not implemented in `simctl`, it is implemented within `CoreSimulator` with `simctl` providing an accessible way of using this functionality. Having this behaviour implemented at the Framework level so that `Simulator.app` and `simctl` behave identically when using their user interfaces.
## `CoreSimulatorService`

`CoreSimulatorService` is a user-level daemon that is bootstrapped by any usage of `SimServiceContext`, effectively any usage of iOS Simulators will cause this service to be created and launched. This is an XPC service contained within the `CoreSimulator.framework` bundle. This service is responsible for starting and managing Simulators.

When using `CoreSimulator` as a client Framework it will transparently communicate with `CoreSimulatorService`. The overwhelming majority of `CoreSimulator` APIs that do meaningful work are essentially performing IO to `CoreSimulatorService`, though the asynchronous nature of this work isn't completely consistent. Some `CoreSimulator` APIs (for instance those associated with [launching an iOS Application](https://github.com/facebook/idb/blob/main/PrivateHeaders/CoreSimulator/SimDevice.h#L75)) do have asynchronous methods, but others (such as the [instantiation of a `SimServiceContext`](https://github.com/facebook/idb/blob/main/PrivateHeaders/CoreSimulator/SimServiceContext.h#L26)) do not. `CoreSimulatorService` still gets much of it's implementation from `CoreSimulator.framework`, touching different areas of the API.

Having the "work" of iOS Simulators performed within the context of share user daemon is likely due to the needs to synchronize and consolidate state. The service is also an effective caching mechanism for runtime and device profiles. There are a few downsides to this approach. Firstly, `CoreSimulatorService` is effectively a single point of failure. If `CoreSimulatorService` becomes stuck, or a client of `CoreSimulator` exhibits pathological behaviour, then all iOS Simulator functionality on a given host will fail. iOS Simulator functionality will effectively halt until `CoreSimulatorService` restarts, either by the hung `CoreSimulatorService` terminating and restarting or via reboot.

Secondly, the lifecycle of `CoreSimulatorService` is tied to that of the selected `Xcode`. This means that different versions of Xcode cannot be used concurrently on the same host; `CoreSimulatorService` can only be aware of a single Xcode at any point in time. Switching Xcodes and fetching a new `CoreSimulatorService` (for instance via a `simctl` command) will cause `CoreSimulatorService` to restart, disconnecting existing clients and killing booted Simulators.

## `SimRuntime`

An iOS Simulator Runtime is all of the required components for running an iOS Simulator of a given iOS version. This is a bundle, where the contents closely match the makeup of the files on disk on a physical device. This includes binaries that are compiled for the host architecture (x86_64 in the case of Intel Macs, ARM64 in the case of ARM based Macs) as well as Frameworks. The Frameworks within a SimRuntime match those of iOS, instead of those of the macOS host. There are often subtle differences in the iOS and macOS APIs, even within the same Framework. A single `SimRuntime` represents a single iOS version.

Each version of Xcode is bundled with `SimRuntime`s for the most recent iOS version that is relevant for the Xcode version across iOS, tvOS and watchOS. However, additional iOS Versions can be supported on a given version of Xcode via the "Components" section within `Xcode.app`. These bundles are then installed into `/Library/Developer/CoreSimulator/Profiles/Runtimes` on the host system. Runtime bundles are backwards, but not forwards compatible. For example, Xcode 11 has support for iOS 13 (the latest iOS version associated with this Xcode version) and earlier versions of iOS, but not for iOS 14.

## `launchd_sim`

iOS, like macOS has `launchd` as it's "root process" (often PID 1). However, iOS Simulators have their own version of `launchd` as a root process. This `launchd_sim` is effectively the "root process" of the iOS Simulator runtime, but not of the macOS host. This `launchd_sim` is required by the Simulator OS in order to launch Applications, manage services etc. Each launched iOS Simulator has it's own `launchd_sim` process, launched from the `launchd_sim` within the `SimRuntime`. This also means that processes within this nested `launchd` will only see the processes of the iOS Simulator, rather than all of those of the entire host (including other iOS Simulators running on the same host).

This `launchd_sim` can be interrogated the same as the `launchd` of the host, provided that the `launchctl` called is spawned within the `launchd_sim` of the iOS Simulator.

## Device Sets

A Device Set is essentially a directory that contains a number of created iOS Simulators. The "Default Device Set" is located at `~/Library/Developer/CoreSimulator/Devices`, this is the device set that is used by `Xcode.app`.

Custom device sets can be placed at any location on disk. This is useful for isolating the filesystems of created iOS Simulators from each other. For instance, if there are independent processes managing iOS Simulators on the same host it can be worthwhile having each of these processes manage their own device sets to prevent data races.

There is also an `XCTestDevices` directory at `~/Library/Developer/XCTestDevices`. This is the set of Simulators that are used by `xcodebuild`, distinct from the user interface. This means that `xcodebuild` can manage and use it's own set of iOS Simulators, independent of the Xcode UI. This may exist for a similar reasons to why custom device sets are practical for automation scenarios. It would also be a confusing user experience if an iOS Simulator that was being used within `xcodebuild` was using an iOS Simulator that a user was using via Xcode when running UI Tests.

## `Simulator.app`

This is the "Simulator" Application with which most developers will be familiar with. This Application effectively mirrors the state of launched iOS Simulators within `CoreSimulatorService`. It is not an essential part of booting and managing iOS Simulators; iOS Simulators can be booted and used without a `Simulator.app` launched for it. This makes using Simulators more practical in automation scenarios where a running macOS Application representing the iOS Simulator is not important or even desirable.

The Simulator Application will default to showing all iOS Simulators that are within the "Default Device Set". This means that booted iOS Simulators within "Custom Device Sets" will not be displayed within `Simulator.app`.

The functionality within this Application is largely implemented within `CoreSimulator.framework` and `SimulatorKit.framework`, with the UI implemented directly within the application itself.

## Framebuffers via `IOSurface`

The screen from an iOS Simulator is rendered, regardless of whether there is an iOS Simulator application that is presenting this within a IO. An iOS Simulator can be launched independently of `Simulator.app`, since Simulators are kept alive by `CoreSimulatorService`.

In order for other Applications (mainly `Simulator.app`, but also for video recording within `simctl`) to get the iOS Simulator's Framebuffer for rendering, `CoreSimulator` can access the `IOSurface` of the screen of an iOS Simulator. A Simulator can have many screens, for instance when Simulating CarPlay and the main screen at the same time.

An `IOSurface` is an object that wraps a Framebuffer, with the contents of the Framebuffer being located within GPU memory. This `IOSurface` can be read and inspected across process boundaries. The iOS Simulator uses this `IOSurface` as the backing Framebuffer for it's view of an iOS Simulator.

`IOSurface` objects are also easily convertable to ["Pixel Buffer" types that are used in video encoding](https://github.com/facebook/idb/blob/d587458fa2188fbcdd3f71fcbb2a131903cfa5f2/FBSimulatorControl/Framebuffer/FBSimulatorVideoStream.m#L398). This allows `FBSimulatorControl` to implement video encoding of an iOS Simulator's Framebuffer in a way that avoids large copies of bitmap framebuffers on a per-frame basis.

## `IndigoHID`

"Indigo" is a service present in the iOS Simulator that is used inside `Simulator.app` to synthesize "Input Events" that are understood within the iOS Simulator. This service is how clicking on the UI of the `Simulator.app` translate into touches within the iOS Simulator.

This uses "mach" IPC, where data structures are sent over a channel using `mach_msg_send`. These data structures are defined through the "Mach Interface Generator", which get compiled out of the `Simulator.app` binary. As such, `FBSimulatorControl`'s understanding of the layout and values in these data structures [are understood through reverse engineering](https://github.com/facebook/idb/blob/main/PrivateHeaders/SimulatorApp/Indigo.h#L40).

The reverse engineering of this protocol, allows `FBSimulatorControl` to expose APIs that allow sending of touch events directly to the iOS Simulator without using Accessibility APIs in a UI Test. The combination of video streams and APIs for sending input events allows for the building of applications that expose a remote iOS Simulator.

## `SimulatorKit.framework`

This is another macOS Framework that is used in iOS Simulator management. This Framework is not installed to the System, it is bundled within Xcode. Instead, this Framework is more used to implement functionality within `Simulator.app`.

For example, this Framework contains some of the `Indigo` client functionality for sending input events.
